/**
  CNOK project, Anyang Normal University, IMP-CAS
  \class TAExpData
  \brief error propagation
  \author SUN Yazhou, asia.rabbit@163.com
  \since 2022/05/23
  \date Last modified: 2022/05/23 by SUN Yazhou
  \copyright 2020-2023 SUN Yazhou
  \copyright CNOK project, Anyang Normal University, IMP-CAS
*/

#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <cmath>

using std::cout;
using std::endl;

#include "TAExpData.h"

TAExpData::TAExpData(double val, double err): fVal(val), fErr(err){}
TAExpData::TAExpData(const TAExpData &data){ *this = data; }
TAExpData &TAExpData::operator=(const TAExpData &data){
	if(&data == this) return *this;

	fVal = data.fVal;
	fErr = data.fErr;

	return *this;
} // end member function operator=

void TAExpData::print() const{
	cout << std::fixed;
	cout << std::setprecision(4);
	const double rel = fErr / fVal * 100.;
	cout << fVal << "(" << fErr << ", " << rel << "%)";
} // end member function Print
std::ostream &operator<<(std::ostream &os, const TAExpData &data){
	// os << data.fVal;
	const double rel = data.fErr / data.fVal * 100.;
	os << data.fVal << "(" << data.fErr << ", " << rel << "%)";
	return os;
} // end member function operator<<
const TAExpData &TAExpData::operator+=(const TAExpData &data){
	fVal += data.fVal;
	fErr = sqrt(fErr*fErr + data.fErr*data.fErr);

	return *this;
} // end member function operator+=
const TAExpData &TAExpData::operator-=(const TAExpData &data){
	fVal -= data.fVal;
	fErr = sqrt(fErr*fErr + data.fErr*data.fErr);

	return *this;
} // end member function operator-=
const TAExpData &TAExpData::operator*=(const TAExpData &data){
	const double prod = fVal*data.fVal;
	fErr = sqrt(pow(fErr*data.fVal, 2) + pow(data.fErr*fVal, 2));
//	fErr = fabs(prod)*sqrt(pow(fErr/fVal, 2) + pow(data.fErr/data.fVal, 2));
	fVal = prod;

	return *this;
} // end member function operator*=
const TAExpData &TAExpData::operator/=(const TAExpData &data){
	const double rate = fVal/data.fVal;
	fErr = fabs(rate)*sqrt(pow(fErr/fVal, 2) + pow(data.fErr/data.fVal, 2));
	fVal = rate;

	return *this;
} // end member function operator/=
TAExpData TAExpData::operator+(const TAExpData &data) const{
	return TAExpData(*this) += data;
} // end member function operator+
TAExpData TAExpData::operator-(const TAExpData &data) const{
	return TAExpData(*this) -= data;
} // end member function operator-
TAExpData TAExpData::operator*(const TAExpData &data) const{
	return TAExpData(*this) *= data;
} // end member function operator*
TAExpData TAExpData::operator/(const TAExpData &data) const{
	return TAExpData(*this) /= data;
} // end member function operator/

TAExpData Sqrt(const TAExpData &p){
	const double val = sqrt(p.val());
	const double err = p.err() / (2.*val);
	return TAExpData(val, err);
} // end member function Sqrt
TAExpData Log(const TAExpData &p){
	const double val = log(p.val());
	const double err = p.err() / p.val();
	return TAExpData(val, err);
} // end member function Log
TAExpData operator+(double x, const TAExpData &p){
	return TAExpData(x+p.val(), p.err());
} // end member function operator+
TAExpData operator-(double x, const TAExpData &p){
	return TAExpData(x-p.val(), p.err());
} // end member function operator-
TAExpData Exp(const TAExpData &p){
	const double ep = exp(p.val());
	return TAExpData(ep, ep*p.err());
} // end member function Exp

TAExpData operator*(double k, const TAExpData &data){
	return data*TAExpData(k, 0.);
} // end member function operator*

TAExpData operator/(double k, const TAExpData &data){
	return data/TAExpData(k, 0.);
} // end member function operator/
